﻿using System;
using System.Collections.Generic;
using System.Windows.Media;
using Bling.DSL;
using Bling.Util;
using Bling.Core;
using Bling.Angles;
using Bling.Matrices;

namespace Bling.NewGraphics {
  // identification possibilities:
  // By vertex or instance ID, computations must be resolved in VS.
  // By primitive ID, computations cannot be resolved in VS but can be resolved in GS/PS.
  // By strip ID, computations are resolved in GS. 
  // By primitive ID + offset, primitive relative addressing, computations are resolved in VS for use only in GS. 

  public abstract class BaseTopology<D> where D : Dim1D<D> {
    public IntBl PrimitiveId, VertexId; // how did we identify primitives and vertices?
    public BaseTopology() { }
  }
  public class ExplicitTopology<D> : BaseTopology<D> where D : Dim1D<D> {
    public IntBl InstanceId; // how do we identify indices?
    public FlexNumFuncBl<IntBl, IntBl> RelativeVertexId; // how did we identify vertices relative to primitives?
    // if an expression refers to vertex id, we then "adjust" it to be primitive relative (and maximize that of course!)
    // pixels don't have ids, instead we interpolate from vertex ids and force them into the pixel shader if necessary.

    public IntBl VertexCount, InstanceCount = 1, PrimitiveCount;
    public IntBl<D> Indices = null;
    public ExplicitTopology() {}
  }
  // create a derived topology, we can refer to vertex id but in the geometry shader we can't
  // access vertex id's, so vertex id stuff must be resolved in...the vertex shader and passed
  // to the geometry shader as parameters.
  // For a derived topology, color/position must be keyed to Strip.Id, but can also refer to
  // primitive id and vertex id. or...vertex id has to be relative to primitive id. 
  public abstract class ComputedTopology<D> : BaseTopology<D>
    where D : Dim1D<D> {
    public IntBl StripId; // how do we identify strips that we emit? ordered in sequence.
    public IntBl StripVertexId; // how do we identify vertices relative to strips? Again, ordered in sequence.
    // StripVertexId is not the same as VertexId as it is relative to strips.
    // In a GS, we can refer to primitives, vertices relative to primitives, this relationship extends through strip
    // and strip vertex ids. We can't refer to vertex id directly...but...we can revaluate expressions, e.g.,
    // if e refers to vertex-id, we can compute it in the context of a primitive relative vertex. 
    public class Strip {
      public BoolBl Guard = true; // whether to emit this strip or not, we can control that dynamically. 
      public IntBl Count = Dim1D<D>.Count; // how many vertices to emit
    }
    public readonly List<Strip> Strips = new List<Strip>();
  }
  public class DerivedTopology<D, E> : ComputedTopology<D>
    where D : Dim1D<D>
    where E : Dim1D<E> {
    public readonly BaseTopology<E> Parent;
    public DerivedTopology(BaseTopology<E> Parent) { this.Parent = Parent; }
    public bool StreamOut = false; // should we stream out.
  }
  // this is a weird one....only support points, can render out, we can refer to properties by name.
  public class StepTopology : BaseTopology<D1> {
    // only pull in the computations that we need.
    public IntBl MaxVertexCount = 1;
    public IntBl InitCount = 1;
  }


  // Theoretically, we could transform a geometry multiple times, 
  // where the primitive of a first defined geometry is identified
  // the primitive-id of a derived geometry does not replace the primitive id of the parent geometry.
  // likewise for vertex-id of a derived geometry, which actually isn't that useful

  // thought experiment, convert a line into two triangles, perp to the line.
  // so 4 vertices in one strip. the new position is 
  //   position[vertexId => StripVertexId/2] + 
  //     perp * (StripVertexId % 2 == StripVertexId / 2) ? -1 : +1
  // do we need to modify the normal? no, not really...
  // position[0] and position[1], define the position
  // we could also blanket reassign values. 

  public struct RenderEntry<D> where D : Dim1D<D> {
    public BaseTopology<D> Topology;
    public Double4Bl Position;
    public ColorBl Color;
  }
  public interface IDevice {
    // render directly.
    void Render<D>(RenderEntry<D> Entry) where D : Dim1D<D>;
  }
  // what does non-stream-out geometry look like? 
}


namespace Bling.NewGraphics {


  // Render:- VertexCount, InstanceCount, Position, Color, Indices

  public interface IGeometry {
    IntBl Duplicated { get; }
    IntBl GetDuplicate(IntBl N);
    void AddDuplicate(IntBl N);
  }
  public interface ITopology<D> : IGeometry where D : Dim1D<D> {
    IntBl VertexCount { get; set; }
    IntBl PrimitiveCount { get; set; }
    IntBl<D> Indices { get; set; }
  }
  public interface IHasUV : IGeometry {
    Double2Bl UV { get; set; }
  }

  public interface IHasTransform : IGeometry {
    Matrix4Bl Transform { get; }
    void PrependTransform(Matrix4Bl Transform);
  }
  public interface IHasPosition : IHasTransform {
    Double3Bl Position { get; set; }
    Double3Bl TransformedPosition { get; }
  }
  public interface IHasNormal : IHasTransform {
    Double3Bl TransformedNormal { get; }
    // we want to allow for multiple normals
    void AddNormal(Double3Bl Normal);
  }
  public interface IHasTangent : IHasNormal {
    Double3Bl Tangent { get; set; }
    Double3Bl TransformedTangent { get; }
  }
  public interface IHasColor : IGeometry { // all pixels have a color
    ColorBl Color { get; set; }
  }
  public interface ICanRender : IHasPosition, IHasColor { }

  public interface IMaterial { }
  public interface IHasLight : IHasColor, IHasNormal {
    void AddMaterial(IMaterial Material);
  }

}